// go:build linux

package main

import (
	"fmt"
	"net"
	"os"
	"syscall"
	"unsafe"

	io_uring "gitee.com/childewang/iouring-go"
	iouring_syscall "gitee.com/childewang/iouring-go/syscall"
)

const readSize = 1024

var (
	iour *io_uring.IOUring
	// resulter chan io_uring.Result
)

type ioData struct {
	read        int
	firstOffset uint64
	offset      uint64
	firstLen    uint64
	iov         []syscall.Iovec
}

func main() {
	if len(os.Args) != 2 {
		fmt.Printf("Usage: %s <host:port>\n", os.Args[0])
		return
	}

	params := iouring_syscall.IOURingParams{
		Flags: uint32(0),
	}

	var err error
	iour, err = io_uring.New(1024, &params)
	if err != nil {
		panic(err)
	}
	defer iour.Close()

	// resulter = make(chan io_uring.Result, 10)

	fd := listenSocket(os.Args[1])
	fmt.Println(fd)

	sqe := iour.GetSQE()
	bs := new([]byte)
	sqe.PrepAccept(fd, (unsafe.Pointer(bs)), 10, 0)

	var read chan struct{}

	go func() {
		for {
			ok := queueRead(iour, uint64(10), 0, int(fd))
			fmt.Println("queueRead：", ok)
			if ok {
				read <- struct{}{}
			}
		}
	}()
	for {
		select {
		case <-read:
			fmt.Println("11111111")
		}
	}
}

func queueRead(iouring *io_uring.IOUring, size uint64, offset uint64, infd int) bool {
	data := new(ioData)

	sqe := iouring.GetSQE()
	if sqe == nil {
		return false
	}

	data.offset = offset
	data.firstOffset = offset

	bs := new([]byte)

	iovs := []syscall.Iovec{
		{Base: (*byte)(unsafe.Pointer(bs)),
			Len: uint64(size)},
	}
	data.iov = iovs
	data.firstLen = size

	fmt.Printf("data: %+v\n", data)

	sqe.PrepReadv(infd, iovs, 1, int64(offset))
	sqe.SetData(uint64(uintptr(unsafe.Pointer(data))))

	return true
}

// func accept(result io_uring.Result) error {
// 	prepAccept := io_uring.Accept(result.Fd()).WithCallback(accept)
// 	if _, err := iour.SubmitRequest(prepAccept, resulter); err != nil {
// 		panicf("submit accept request error: %v", err)
// 	}

// 	if err := result.Err(); err != nil {
// 		panicf("accept error: %v", err)
// 	}

// 	connFd := result.ReturnValue0().(int)
// 	sockaddr := result.ReturnValue1().(*syscall.SockaddrInet4)

// 	clientAddr := fmt.Sprintf("%s:%d", net.IPv4(sockaddr.Addr[0], sockaddr.Addr[1], sockaddr.Addr[2], sockaddr.Addr[3]), sockaddr.Port)
// 	fmt.Printf("Client Conn: %s\n", clientAddr)

// 	buffer := make([]byte, readSize)
// 	prep := io_uring.Read(connFd, buffer).WithInfo(clientAddr).WithCallback(read)
// 	if _, err := iour.SubmitRequest(prep, resulter); err != nil {
// 		panicf("submit read request error: %v", err)
// 	}
// 	return nil
// }

// func read(result io_uring.Result) error {
// 	clientAddr := result.GetRequestInfo().(string)
// 	if err := result.Err(); err != nil {
// 		panicf("[%s] read error: %v", clientAddr, err)
// 	}

// 	num := result.ReturnValue0().(int)
// 	buf, _ := result.GetRequestBuffer()
// 	content := buf[:num]

// 	connPrintf(clientAddr, "read byte: %v\ncontent: %s\n", num, content)

// 	prep := io_uring.Write(result.Fd(), content).WithInfo(clientAddr).WithCallback(write)
// 	if _, err := iour.SubmitRequest(prep, resulter); err != nil {
// 		panicf("[%s] submit write request error: %v", clientAddr, err)
// 	}
// 	return nil
// }

// func write(result io_uring.Result) error {
// 	clientAddr := result.GetRequestInfo().(string)
// 	if err := result.Err(); err != nil {
// 		panicf("[%s] write error: %v", clientAddr, err)
// 	}
// 	connPrintf(clientAddr, "write successful\n")

// 	prep := io_uring.Close(result.Fd()).WithInfo(clientAddr).WithCallback(close)
// 	if _, err := iour.SubmitRequest(prep, resulter); err != nil {
// 		panicf("[%s] submit write request error: %v", clientAddr, err)
// 	}
// 	return nil
// }

// func close(result io_uring.Result) error {
// 	clientAddr := result.GetRequestInfo().(string)
// 	if err := result.Err(); err != nil {
// 		panicf("[%s] close error: %v", clientAddr, err)
// 	}
// 	connPrintf(clientAddr, "close successful\n")
// 	return nil
// }

func listenSocket(addr string) int {
	fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)
	if err != nil {
		panic(err)
	}

	tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		panic(err)
	}

	sockaddr := &syscall.SockaddrInet4{Port: tcpAddr.Port}
	copy(sockaddr.Addr[:], tcpAddr.IP.To4())
	if err := syscall.Bind(fd, sockaddr); err != nil {
		panic(err)
	}

	if err := syscall.Listen(fd, syscall.SOMAXCONN); err != nil {
		panic(err)
	}
	return int(fd)
}

func panicf(format string, a ...interface{}) {
	panic(fmt.Sprintf(format, a...))
}

func connPrintf(addr string, format string, a ...interface{}) {
	prefix := fmt.Sprintf("[%s]", addr)
	fmt.Printf(prefix+format, a...)
}
